package com.github.houkunlin.model;

import cn.hutool.core.collection.CollectionUtil;
import com.github.houkunlin.extension.DataSourceService;
import com.github.houkunlin.util.ProjectUtils;
import com.github.houkunlin.vo.impl.RootModel;
import com.intellij.database.dataSource.DataSourceSchemaMapping;
import com.intellij.database.dataSource.DataSourceStorage;
import com.intellij.database.dataSource.LocalDataSource;
import com.intellij.database.dataSource.LocalDataSourceManager;
import com.intellij.database.model.DasDataSource;
import com.intellij.database.model.DasModel;
import com.intellij.database.model.DasNamespace;
import com.intellij.database.model.DasTable;
import com.intellij.database.model.basic.BasicElement;
import com.intellij.database.model.basic.BasicModMixinElement;
import com.intellij.database.psi.DbDataSource;
import com.intellij.database.psi.DbPsiFacade;
import com.intellij.database.psi.DbTable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.JBIterable;
import lombok.Getter;
import org.jdesktop.swingx.combobox.ListComboBoxModel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DataTableModel implements DataSourceStorage.Listener {

    private final Project project;

    final Map<String, DasTable> allTables = new ConcurrentHashMap<>(16);

    final List<String> tableNameList = new ArrayList<>();

    String selectedTableName;

    @SuppressWarnings("unchecked")
    @Getter
    final ComboBoxModel<String> listComboBoxModel = new ListComboBoxModel<String>(tableNameList);

    public DataTableModel(Project project){
        this.project = project;
        dataSourceModelUpdated();
        project.getMessageBus().connect().subscribe(DataSourceStorage.TOPIC, this);
    }

    public ComboBox<String> getDataSourceComboBoxPanel(){
        ComboBox<String> dataSourceComboBox = new ComboBox<String>(listComboBoxModel,500);
        dataSourceComboBox.addActionListener(e -> {
            selectedTableName = (String) dataSourceComboBox.getSelectedItem();
        });
        dataSourceComboBox.setSwingPopup(false);
        return dataSourceComboBox;
    }

    private JBIterable<DasTable> getTables(DasDataSource dataSource){
        return dataSource.getModel().traverser().expandAndSkip(Conditions.instanceOf(DasNamespace.class))
                         .filter(DasTable.class);
    }

    private List<String> getAllTableNameBySort(Map<String, DasTable> allTables){
        if(CollectionUtil.isEmpty(allTables)){
            return Collections.emptyList();
        }
        // 表排前面，视图排后面
        List<String> tableList = new ArrayList<>();
        List<String> viewList = new ArrayList<>();
        for(String name : allTables.keySet()){
            if(name.endsWith("table")){
                tableList.add(name);
            }else{
                viewList.add(name);
            }
        }
        // 排序后进行拼接
        Collections.sort(tableList);
        Collections.sort(viewList);
        tableList.addAll(viewList);
        return tableList;
    }

    public RootModel getSelectedModel(){
        if(StringUtil.isNotEmpty(selectedTableName)){
            DasTable dasTable = allTables.get(selectedTableName);
            DbTable dbTable = DataSourceService.getDataTable(dasTable);
            return RootModel.getRootModel(dbTable);
        }
        return null;
    }


    public synchronized void dataSourceModelUpdated(){
        findAllTablesInProject();
        updateTableNameList(allTables);
        DataSourceService dataSourceService = DataSourceService.getInstance();
        List<DbDataSource> dataSources = DbPsiFacade.getInstance(ProjectUtils.getCurrProject()).getDataSources();
        dataSourceService.refreshTable(dataSources);
    }

    @NotNull
    private Map<String, DasTable> findAllTablesInProject(){
        LocalDataSourceManager instance = LocalDataSourceManager.getInstance(project);
        List<LocalDataSource> dataSources = instance.getDataSources();
        allTables.clear();
        for(LocalDataSource dataSource : dataSources){
            for(DasTable table : getTables(dataSource)){
                BasicElement parent = ((BasicModMixinElement) table).getParent();
                if(parent != null){
                    DataSourceSchemaMapping schemaMapping = dataSource.getSchemaMapping();
                    boolean introspected = schemaMapping.isIntrospected(parent);
                    if(!introspected){
                        continue;
                    }
                }
                allTables.put(table.toString(), table);
            }
        }
        return allTables;
    }

    private void updateTableNameList(Map<String, DasTable> allTables){
        tableNameList.clear();
        tableNameList.addAll(getAllTableNameBySort(allTables));
    }

    public void dataSourceModelLoaded(@NotNull LocalDataSource dataSource, @NotNull DasModel model){
        dataSourceModelUpdated();
    }

    public void dataSourceAdded(@NotNull LocalDataSource dataSource){
        dataSourceModelUpdated();
    }

    public void dataSourceRemoved(@NotNull LocalDataSource dataSource){
        dataSourceModelUpdated();
    }

    public void dataSourceChanged(@Nullable LocalDataSource dataSource){
        if(dataSource != null){
            dataSourceModelUpdated();
        }
    }
}
